﻿// Copyright (c) 2009 Kjetil Eraker Samuelsen
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
// All other rights reserved.

using System;
using System.Net;
using System.Globalization;
using System.Text;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;

namespace Ksamuelsen.Services.Vonage {
    public static class Click2Call {
        public enum Service {
            Click2Call,
            ContactCenter
        }

        private static string[] _status_ok = Properties.Resources.VONAGE_CCURL_STATUS_OK.Split(';');
        private static Service  _service   = Service.Click2Call;


        public static Service VonageService {
            get { return _service; }
            set { _service = value; }
        }

        public static void MakeCall(string username, string password, string fromNumber, string toNumber) {
            switch (_service) {
                case Service.Click2Call: MakeCallC2C(username, password, fromNumber, toNumber); break;
                case Service.ContactCenter: MakeCallCC(username, password, fromNumber, toNumber); break;
                default: MakeCallC2C(username, password, fromNumber, toNumber); break;
            }
        }


        public static string[] GetNumbers(string username, string password) {
            Uri uri = GetUri(Properties.Resources.VONAGE_URL_GETNUMBERS, Properties.Resources.VONAGE_URL_QRY_GETNUMBERS, username, password);

            using (WebClient client = new WebClient()) {
                string result = client.DownloadString(uri);
                if (String.IsNullOrEmpty(result)) {
                    throw new Click2CallException();
                } else if (result.IndexOf(":") > -1) {
                    throw new Click2CallException(GetErrorCode(result), GetMessage(result));
                }
                return TryFormat(result.Split((new char[] { ',' }), 999, StringSplitOptions.RemoveEmptyEntries));

            }
        }


        private static HttpWebRequest CreateRequest(string url, string content, params Cookie[] cookies) {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            byte[]         buf     = (String.IsNullOrEmpty(content)?null:Encoding.UTF8.GetBytes(content));
            Stream         istream = null;

            request.ContentType     = Properties.Resources.HTTP_CONTENT_TYPE;
            request.Method          = Properties.Resources.HTTP_METHOD;
            request.UserAgent       = Properties.Resources.HTTP_USER_AGENT;
            request.ContentLength   = (buf == null)?0:buf.Length;
            request.CookieContainer = new CookieContainer();

            if ((cookies != null) && (cookies.Length > 0)) {
                foreach (Cookie cookie in cookies)
                    request.CookieContainer.Add(cookie);
            } 

            if (buf != null) {
                try {
                    istream = request.GetRequestStream();
                    istream.Write(buf, 0, buf.Length);
                } finally {
                    if (istream != null)
                        istream.Close();
                }
            }
            return request;

        }

        private static Cookie Login(string username, string password) {
            string          body       = String.Format(Properties.Resources.VONAGE_CCURL_LOGIN_BODY, Uri.EscapeUriString(username), Uri.EscapeUriString(password));
            HttpWebRequest  request    = CreateRequest(Properties.Resources.VONAGE_CCURL_LOGIN, body);
            HttpWebResponse response   = null;
            Cookie          authcookie = null;


            try {
                response   = (HttpWebResponse)request.GetResponse();
                authcookie = response.Cookies[Properties.Resources.VONAGE_CCURL_LOGIN_COOKIE];

                if ((authcookie == null) || (String.IsNullOrEmpty(authcookie.Value)))
                    throw new Click2CallException(Properties.Resources.ERROR_INVALID_AUTH_MSG);

                
            } finally {
                if (response != null)
                    response.Close();
            }

            return authcookie;
        }

        private static string CallService(string url, string content, params Cookie[] cookies) {
            HttpWebRequest  request  = CreateRequest(url, content, cookies);
            HttpWebResponse response = null;
            byte[]          buf      = null;
            Stream          ostream  = null;

            try {
                response = (HttpWebResponse)request.GetResponse();
                if (response.ContentLength > 0) {
                    buf     = new byte[response.ContentLength];
                    ostream = response.GetResponseStream();

                    ostream.Read(buf, 0, buf.Length);
                }
            } finally {
                if (ostream != null)
                    ostream.Close();
            }

            return ((buf == null) || (buf.Length == 0)) ? null : Encoding.UTF8.GetString(buf);
        }

        private static bool IsCallComplete(string result) {
            return (String.IsNullOrEmpty(result)?true:(result.IndexOf(Properties.Resources.VONAGE_CCURL_STATUS_END, StringComparison.CurrentCultureIgnoreCase) > -1));
        }

        private static string ParseStatus(string result) {
            int start = result.IndexOf(Properties.Resources.VONAGE_CCURL_STATUS_MSG, StringComparison.CurrentCultureIgnoreCase);
            int end = (start > -1) ? result.IndexOf("\"", start + Properties.Resources.VONAGE_CCURL_STATUS_MSG.Length) : -1;

            if ((start > -1) && (end > start)){
                start +=  Properties.Resources.VONAGE_CCURL_STATUS_MSG.Length;
                return result.Substring(start, end - start);
            }
            
            return Properties.Resources.ERROR_UNEXPECTED;
        }

        


        private static void MakeCallCC(string username, string password, string fromNumber, string toNumber) {
            Cookie auth = Login(username, password);
            string body = String.Format(Properties.Resources.VONAGE_CCURL_STATUS_BODY, Uri.EscapeUriString(toNumber), Uri.EscapeUriString(fromNumber));
            

            string   result   = CallService(String.Format(Properties.Resources.VONAGE_CCURL_MAKECALL, Uri.EscapeUriString(fromNumber), Uri.EscapeUriString(toNumber)), null, auth);
            DateTime calltime = DateTime.Now;

            while (!IsCallComplete(result)) {
                TimeSpan duration = (DateTime.Now - calltime);
                if (duration.Seconds > 10)
                    throw new Click2CallException(Properties.Resources.ERROR_TIMEOUT);

                result = CallService(Properties.Resources.VONAGE_CCURL_STATUS, body, auth);
                if (!IsCallComplete(result))
                    Thread.Sleep(500);
            }

            string status = String.IsNullOrEmpty(result) ? null : ParseStatus(result);

            if (String.IsNullOrEmpty(status))
                throw new Click2CallException(Properties.Resources.ERROR_UNKNOWN);
            if (!status.EqualsAnyIgnoreCase(_status_ok))
                throw new Click2CallException(result);
        }




        private static void MakeCallC2C(string username, string password, string fromNumber, string toNumber) {
            Uri uri = GetUri(Properties.Resources.VONAGE_URL_MAKECALL, Properties.Resources.VONAGE_URL_QRY_MAKECALL, username, password, fromNumber, toNumber);

            using (WebClient client = new WebClient()) {
                string result = client.DownloadString(uri);
                if (String.IsNullOrEmpty(result)){
                    throw new Click2CallException();
                } else if (!result.StartsWith("000:")) {
                    throw new Click2CallException(GetErrorCode(result), GetMessage(result));
                }
            }

        }

        private static string[] TryFormat(string[] numbers) {
            string region = RegionInfo.CurrentRegion.TwoLetterISORegionName;
            try {
                string   format = Properties.Resources.ResourceManager.GetString("PHONE_NUMBER_FMT_" + region);
                string[] result = new string[numbers.Length];
                
                if (String.IsNullOrEmpty(format))
                    return numbers;

                for (int i = 0, j = numbers.Length; i < j; i++) {
                    double v = Double.Parse("1" + numbers[i]);
                    result[i] = String.Format("{0:" + format + "}", v).Substring(1);
                }
                return result;
            } catch (Exception) {
                return numbers;
            }
            

        }

        private static string GetErrorCode(string result) {
            int pos = result.IndexOf(":");
            if (pos > -1)
                return result.Substring(0, pos);
            return string.Empty;
        }

        private static string GetMessage(string result) {
            int pos = result.IndexOf(":");
            if (pos > -1)
                return result.Substring(pos);
            return result;
        }

        private static Uri GetUri(string method, string query, params object[] parameters) {
            UriBuilder builder = new UriBuilder(method);

            builder.Query = String.Format(query, parameters);
            return builder.Uri;
        }

        internal static bool EqualsAnyIgnoreCase(this string s, params string[] matches) {
            if ((matches == null) || (matches.Length == 0))
                return false;

            foreach (string match in matches)
                if (s.Equals(match, StringComparison.CurrentCultureIgnoreCase))
                    return true;
            return false;
        }

    }
}
